import { Injectable, OnModuleDestroy, Inject } from '@nestjs/common';
import { Queue, Worker, QueueEvents } from 'bullmq';
import { Redis } from 'ioredis';
import publishSubscrib from '../../utils/publishSubscrib';

@Injectable()
export class BullMQService implements OnModuleDestroy {
  private queues: Record<string, Queue> = {};
  private workers: Record<string, Worker> = {};
  private queueEvents: Record<string, QueueEvents> = {};
  private queue: any;

  constructor(
    @Inject('BULLMQ_REDIS')
    private readonly redisClient: Redis
  ) {
    this.queue = null;
  };

  createQueue(name: string) {
    if (!this.queues[name]) {
      this.queues[name] = new Queue(name, {
        connection: this.redisClient,
        defaultJobOptions: {
          removeOnComplete: true,
          removeOnFail: 1000,
        },
      });

      // 可选：初始化队列事件监听
      this.queueEvents[name] = new QueueEvents(name, {
        connection: this.redisClient
      });
    };
    return this.queues[name];
  };

  createWorker(queueName: string, processor: (job: any) => Promise<any>) {
    if (!this.workers[queueName]) {
      this.workers[queueName] = new Worker(
        queueName,
        processor,
        {
          connection: this.redisClient,
          concurrency: 5,
        }
      );

      this.workers[queueName].on('failed', (job, err) => {
        console.error(`Job ${job?.id} failed:`, err);
      });
      this.workers[queueName].on('completed', (job) => {
        console.error(`Job ${job?.id} completed:`, job);
      });
      this.workers[queueName].on('progress', (job, progress) => {
        console.log(`任务 ${job.id} 进度更新: ${progress}%`);
      });
    }
    return this.workers[queueName];
  };

  async addJob(queueName: string, jobData: any, options?: any) {
    this.queue = this.createQueue(queueName);
    return this.queue.add(queueName, jobData, options);
  };

  async getJob(queueName: string, jobId: string) {
    const queue = this.queues[queueName];
    if (!queue) throw new Error(`Queue ${queueName} not found`);

    const job = await queue.getJob(jobId);

    return {
      id: job?.id,
      data: job?.data,
      // state: await job?.getState(),
      // progress: job?.progress,
      // returnValue: job?.returnvalue,
      // failedReason: job?.failedReason
    };
  };

  async onModuleInit() {
    // ...
  };

  async onModuleDestroy() {
    await Promise.all(
      Object.values(this.workers).map(w => w.close())
    );
    // this.redisClient.disconnect();
  };

  /** -------------------------- logic --------------------------- */

  async f_getPlayersCoordinates(job: any) {
    publishSubscrib.publish('WORKER_JOB', job.data);
  };
  async startGame(puid: string) {
    this.createWorker(puid, this.f_getPlayersCoordinates.bind(this));
  };
};